package com.dpf.api.utills;

import com.alibaba.fastjson.JSON;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.*;

public class HttpClientPoolUtil {
    private static final Logger log = LoggerFactory.getLogger(HttpClientPoolUtil.class);

    //region [1、线程池请求]
    public static CloseableHttpClient httpClient = null;

    /**
     * 默认content 类型
     */
    private static final String DEFAULT_CONTENT_TYPE = "text/html";

    /**
     * 默认请求超时时间，单位秒，默认15
     */
    private static final int DEFAUL_TTIME_OUT = 30;

    /**
     * 设置每个路由基础的连接
     */
    private static final int count = 32;

    /**
     * 设置目标主机的最大连接数
     */
    private static final int totalCount = 1000;

    /**
     * 保活时间，单位秒
     */
    private static final int Http_Default_Keep_Time = 60;

    /**
     * 单个路由最大连接数
     */
    private static final int maxPerRoute = 100;


    private final static Object syncLock = new Object();

    /**
     * 初始化连接池
     */
    public static CloseableHttpClient initPools(String hostname, int port) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
        //LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory.getSocketFactory();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                SSLContexts.custom().loadTrustMaterial(null,new TrustSelfSignedStrategy()).build(),
                NoopHostnameVerifier.INSTANCE);
        Registry registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", plainsf).register("https", sslsf).build();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
        cm.setDefaultMaxPerRoute(count);
        cm.setMaxTotal(totalCount);
        HttpHost httpHost = new HttpHost(hostname, port);
        //将目标主机的最大连接数增加
        cm.setMaxPerRoute(new HttpRoute(httpHost), maxPerRoute);
        CloseableHttpClient httpClientTemp = HttpClients.custom()
                .setKeepAliveStrategy(defaultStrategy)
                .setConnectionManager(cm)
                .setRetryHandler(new DefaultHttpRequestRetryHandler(0, false))
                .build();
        return httpClientTemp;
    }

    /**
     * 获取HttpClient对象
     *
     * @param url 请求地址
     * @return
     */
    public static CloseableHttpClient getHttpClient(String url) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        String hostname = url.split("/")[2];
        int port = 80;
        if (hostname.contains(":")) {
            String[] arr = hostname.split(":");
            hostname = arr[0];
            port = Integer.parseInt(arr[1]);
        }
        if (httpClient == null) {
            synchronized (syncLock) {
                if (httpClient == null) {
                    httpClient = initPools(hostname, port);
                }
            }
        }
        return httpClient;
    }

    /**
     * Http connection keepAlive 设置
     */
    private static ConnectionKeepAliveStrategy defaultStrategy = (response, context) -> {
        HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator(HTTP.CONN_KEEP_ALIVE));
        int keepTime = Http_Default_Keep_Time;
        while (it.hasNext()) {
            HeaderElement he = it.nextElement();
            String param = he.getName();
            String value = he.getValue();
            if (value != null && param.equalsIgnoreCase("timeout")) {
                try {
                    return Long.parseLong(value) * 1000;
                } catch (Exception e) {
                    log.error("系统异常",e);
            e.printStackTrace();
                    log.error("format KeepAlive timeout exception, exception:" + e.toString());
                }
            }
        }
        return keepTime * 1000;
    };

    /**
     * 执行http post请求 默认采用Content-Type：application/json，Accept：application/json
     * 执行http get请求
     *
     * @param uri         请求地址
     * @param data        请求数据get请求该参数无效
     * @param methodType  HttpPost.METHOD_NAME或者HttpGet.METHOD_NAME
     * @param contentType 请求类型，默认application/json
     * @param timeOut     超时时间，单位秒
     * @param enCode      编码，默认UTF-8
     * @return
     */
        public static TwoTuple<Integer, String> executeHttp(String uri, String data, String methodType, String contentType, int timeOut, String enCode) {
        long startTime = System.currentTimeMillis();
        CloseableHttpResponse httpResponse = null;
        HttpEntity httpEntity = null;
        HttpRequestBase method = null;
        String responseBody = "";
        TwoTuple<Integer, String> resultTuple = null;

        StringBuilder infoStr = new StringBuilder();
        UUID flagUUid = UUID.randomUUID();
        infoStr.append("请求标识：");
        infoStr.append(flagUUid);
        infoStr.append(" 发起网络请求：Url=");
        infoStr.append(uri);
        infoStr.append(" 请求参数：\n");
        infoStr.append(data);
        infoStr.append(" 请求方式：");
        infoStr.append(methodType);
        infoStr.append(" 发起请求时间戳：");
        infoStr.append(startTime);
        try {
            method = getRequest(uri, methodType, timeOut);
            if (HttpPost.METHOD_NAME.equalsIgnoreCase(methodType)) {
                HttpEntityEnclosingRequestBase postMethod = (HttpEntityEnclosingRequestBase) method;
                if (StringUtils.isEmpty(enCode)) {
                    enCode = "UTF-8";
                }
                if (StringUtils.isBlank(contentType)) {
                    contentType = DEFAULT_CONTENT_TYPE;
                }
                ContentType contentType1 = ContentType.create(contentType, enCode);
                StringEntity stringEntity = new StringEntity(data, contentType1);
                postMethod.setEntity(stringEntity);
                method = postMethod;
            }
            HttpContext context = HttpClientContext.create();
            httpResponse = getHttpClient(uri).execute(method, context);
            httpEntity = httpResponse.getEntity();
            int state = -1;
            if (httpEntity != null) {
                state = httpResponse.getStatusLine().getStatusCode();
                responseBody = EntityUtils.toString(httpEntity, "UTF-8");
            }
            infoStr.append("返回状态码：");
            infoStr.append(state);
            infoStr.append(",响应内容：");
            infoStr.append(responseBody);
            resultTuple = new TwoTuple<>(state, responseBody);
        } catch (Exception e) {
            if (method != null) {
                method.abort();
            }
            log.error("系统异常",e);
            e.printStackTrace();
            infoStr.append("http请求异常");
            infoStr.append(e);
        } finally {
            if (httpEntity != null) {
                try {
                    EntityUtils.consumeQuietly(httpEntity);
                } catch (Exception e) {
                    log.error("系统异常",e);
            e.printStackTrace();
                    infoStr.append("关闭" + methodType + "请求异常,");
                    infoStr.append(e);
                }
            }
            try {
                if (httpEntity != null) {
                    httpResponse.close();
                }
            } catch (IOException e) {
                log.error("系统异常",e);
            e.printStackTrace();
            }
            infoStr.append(" , cost time(ms):");
            infoStr.append(System.currentTimeMillis() - startTime);
            log.info(infoStr.toString());
        }
        return resultTuple;
    }

    /**
     * 执行http post请求 默认采用Content-Type：application/json，Accept：application/json
     * 执行http get请求
     *
     * @param uri         请求地址
     * @param parameter   请求数据get请求该参数无效
     * @param contentType 请求类型，默认application/json
     * @param methodType  HttpPost.METHOD_NAME或者HttpGet.METHOD_NAME
     * @param timeOut     超时时间，单位秒
     * @return
     */
    public static TwoTuple<Integer, String> executeHttp(String uri, Map parameter, String methodType, String contentType, int timeOut, String enCode) {
        String params = JSON.toJSONString(parameter);
        return executeHttp(uri, params, methodType, contentType, timeOut, enCode);
    }

    /**
     * 创建请求
     *
     * @param uri        请求url
     * @param methodName 请求的方法类型
     * @param timeout    超时时间
     * @return
     */
    private static HttpRequestBase getRequest(String uri, String methodName, int timeout) {
        HttpRequestBase method;
        if (timeout <= 0) {
            timeout = DEFAUL_TTIME_OUT;
        }
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(timeout * 1000)
                .setConnectTimeout(timeout * 1000)
                .setConnectionRequestTimeout(timeout * 1000)
                .setExpectContinueEnabled(false)
                .build();

        if (HttpPut.METHOD_NAME.equalsIgnoreCase(methodName)) {
            method = new HttpPut(uri);
        } else if (HttpPost.METHOD_NAME.equalsIgnoreCase(methodName)) {
            method = new HttpPost(uri);
        } else if (HttpGet.METHOD_NAME.equalsIgnoreCase(methodName)) {
            method = new HttpGet(uri);
        } else {
            method = new HttpPost(uri);
        }
        method.setConfig(requestConfig);
        return method;
    }

    /**
     * map转为string参数
     *
     * @param parameter
     * @return
     */
    private static String getStringFromMap(Map parameter) {
        StringBuffer sb = new StringBuffer();
        String params = "";
        try {
            // 编码请求参数
            if (parameter.size() == 1) {
                for (Object name : parameter.keySet()) {
                    sb.append(name).append("=").append(parameter.get(name));
                }
                params = sb.toString();
            } else {
                for (Object name : parameter.keySet()) {
                    sb.append(name).append("=").append(parameter.get(name))
                            .append("&");
                }
                String temp_params = sb.toString();
                params = temp_params.substring(0, temp_params.length() - 1);
            }
        } catch (Exception ex) {
            log.error("系统异常", ex);
            ex.printStackTrace();
            return null;
        }
        return params;
    }
    //endregion
}